package com.example.demo.controller;

import com.example.demo.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Objects;
import java.util.UUID;

/**
 *  怎么学 Spring MVC??
 *  学习 Spring MVC 我们只需要掌握 3 个功能.
 *
 *  1. 连接的功能：将⽤户（浏览器）和 Java 程序连接起来，
 *     也就是访问⼀个地址能够调⽤到我们的Spring 程序
 *  2. 获取参数的功能：⽤户访问的时候会带⼀些参数，在程序中要想办法获取到参数。
 *  3. 输出数据的功能：执⾏了业务逻辑之后，要把程序执⾏的结果返回给⽤户.
 *
 */

@Slf4j
@Controller // 让框架启动时加载当前类(只有加载好了的类, 别人才能使用).
@ResponseBody // 告诉程序返回的是一个数据而不是页面.
// @RestController
// @Controller + @ResponseBody = @RestController.
@RequestMapping("/test") // 路由注册
public class TestController {
    // a). @RequestMapping("/hi") // 这么写当前方法 post, get 请求都支持
    // b). 设置当前方法只能支持 post 请求.
    // @RequestMapping(value = "/hi", method = RequestMethod.POST)  写法1
    // c). @PostMapping("/hi") // 相当于写法1. -> 设置当前方法只能支持 post 请求.
    // d). 设置当前方法只能支持 get 请求.
    @GetMapping("/hi")
    public String sayHi() {
        return "Hi, Spring MVC";
    }

    // 2.1 传递普通参数
    @GetMapping("/hello")
    public String sayHello(String name, Integer age) {
        // 注意:
        // 1. 参数名称要相同, 与顺序无关.
        // 2. 一定要传包装类, 不要传基础类型.
        // url: http://localhost:8080/test/hello?name=张三&age=15
        return "hello, " + name + ", age, " + age;
    }

    // 2.2 接收普通对象
    @GetMapping("/user-show")
    public String getUser(User user) {
        // url: http://localhost:8080/test/user-show?id=5&name=张三&age=15
        // id, name, age 这三个参数顺序不做要求
        return user.toString();
    }

    // 2.3 参数重命名
    @GetMapping("/show-time")
    // public String shoeTime(String t, String t2) {
    // t, t2 不具有代表性, 将其重命名
    // @RequestParam 分别将 t, t2 重命名为 startTime, endTime,
    // required = false 设置该属性不是非必需的, 可以不传, required 默认为 true.
    public String shoeTime(@RequestParam(value = "t", required = false) String startTime,
                           @RequestParam("t2")String endTime) {
        // 使用 startTime, endTime...
        // url: http://localhost:8080/test/show-time/?t=2023.2.17&t2=20232.28
        return "开始时间: " + startTime + " | 结束时间: " + endTime;
    }

    // 2.4 @RequestBody 接收 Json 对象. (和第三方系统通讯时常见的场景).
    @GetMapping("/json")
    public String getJsonUser(@RequestBody User user) {
        // 参数列表里 @RequestBody 注解必不可少.
        // 使用 postman 向 http://localhost:8080/test/json
        // 以 json 格式 ({"id": 2,"name": "张三","age": 18}) 发送请求
        return user.toString();
    }

    // 2.5 从 url(baseURL) 中直接获取参数.
    // 这里的 url 不再像 http://localhost:8080/test?age=15 这种有问号的,
    // 而是类似 http://localhost:8080/test/15, url 中的参数就直接是 15.
    @RequestMapping("/login/{username}/{password}")
    // name 和 password 都加了{ }, 表示它们是 url 中的参数.
    // 要取到这两个参数, 需要借助 @PathVariable 注解.
    // @PathVariable String username --> 将url参数 username 取出来赋值给变量 name.
    public String login(@PathVariable("username") String name,
                        @PathVariable("password") String pwd) {
        // url: http://localhost:8080/test/login/张三/123456
        return "用户名: " + name + ", 密码: " + pwd;
    }
    @RequestMapping("/show/{username}/and/{password}")
    public String login2(@PathVariable("username") String name,
                        @PathVariable("password") String pwd) {
        // url: http://localhost:8080/test/show/李四/and/123456
        return "用户名: " + name + ", 密码: " + pwd;
    }


    // 2.6 使用 @RequestPart 注解上传文件.
    @RequestMapping("/file")
    // myfile 表示key 值, value 是文件.
    public String upFile(@RequestPart("myfile")MultipartFile multipartFile) throws IOException {
        // 这里写死了....
        String filePath = "D:\\image\\img.jpg";
        multipartFile.transferTo(new File(filePath));
        return "文件上传成功: " + filePath;
        // url: http://localhost:8080/test/file --> 使用 postman 上传文件.
    }

    @Value("${myFilePath}")
    private String path;
    @RequestMapping("/up-my-file")
    // myfile 表示key 值, value 是文件.
    public String upMyFile(@RequestPart("myfile")MultipartFile multipartFile) throws IOException {
        // 保存的根目录
        // StringBuilder filePath = new StringBuilder("D:\\image\\");
        // 从配置文件中获取根目录
        StringBuilder filePath = new StringBuilder(path);
        // 根目录 + 唯一的文件名.
        // .toString().replace("-","") 是为了将生成的名字中的 - 去掉(换成空字符串).
        filePath.append(UUID.randomUUID().toString().replace("-",""));
        // 根目录 + 唯一的文件名 + 文件后缀名
        // Objects.requireNonNull 是为了防止 multipartFile 为空.
        filePath.append(Objects.requireNonNull(multipartFile.getOriginalFilename()).
                substring(multipartFile.getOriginalFilename().lastIndexOf(".")));

        multipartFile.transferTo(new File(filePath.toString()));
        return "文件上传成功: " + filePath;
        // url: http://localhost:8080/test/up-my-file --> 使用 postman 上传文件.
    }


    // 2.7 使用 servlet 获取 url 中的参数
    @GetMapping("/getparam")
    // spring MVC 内置了 HttpServletRequest 和 HttpServletResponse
    public String getParam(HttpServletRequest request) {
        return request.getParameter("username");
        // url: http://localhost:8080/test/getparam?username=lisi
    }


    // 2.8 获取 cookie
    // 2.8.1 传统方式获取(servlet) cookie(只能获取全部cookie)
    @RequestMapping("/getcookie")
    public String getCookie(HttpServletRequest request) {
        // 先在浏览器 "伪造" 几个 cookie.
        Cookie[] cookies = request.getCookies(); // 获取到所有的 cookie
        for (Cookie cookie : cookies) {
            // 以日志的形式把 cookie 打印出来 -> 在类上加 @Slf4j 注解.
            log.error(cookie.getName() + " : " + cookie.getValue());
        }
        String userAgent = request.getHeader("User-Agent");
        return "get cookie and header success! | header: " + userAgent;
        // url: http://localhost:8080/test/getcookie
    }
    // 2.8.2 更简单的方式获取 cookie (可以获取具体的某个 cookie )
    @RequestMapping("/getcookie2")
    // @CookieValue(value = "lisi", required = false) : 如果lisi 这个 cookie  存在, 就把它的 value 值赋给 val.
    //      value = "lisi" 中的 lisi 是 cookie 中的 key
    //      required = false : 如果 lisi 这个 cookie 不存在, 不会报错, 直接获取到 null.
    public String getCookie2(@CookieValue(value = "lisi", required = false) String val) {
        return "Cookie Value: " + val;
        // url: http://localhost:8080/test/getcookie2
    }

    // 2.9 简洁获取 Header—@RequestHeader
    @RequestMapping("/getheader")
    public String getHeader(@RequestHeader("User-Agent") String userAgent) {
        return userAgent;
        // url: http://localhost:8080/test/getheader
    }


    // 2.10.1 Session 存储
    // Session 存储和 Servlet 类似，是使⽤ HttpServletRequest 中获取的, 没有注解.
    @RequestMapping("/setsession")
    public String setSession(HttpServletRequest request) {
        HttpSession session = request.getSession();
        // 设置的session 的 key 为 userinfo, value 为 userinfo-value.
        session.setAttribute("userinfo", "userinfo-value");
        return "set session success!";
        // url: http://localhost:8080/test/setsession
    }


    // 2.10.2 Session 的获取
    // 读取 Session 可以使⽤ HttpServletRequest
    @RequestMapping("/getsession")
    public String getSession(HttpServletRequest request) {
        // 如果 session 不存在，不会⾃动创建
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("userinfo") != null) {
            return session.getAttribute("userinfo").toString();
        } else {
            return "暂无 session 信息";
        }
        // url: http://localhost:8080/test/getsession
    }
    // 更简单的读取 session
    @RequestMapping("/getsession2")
    // 如果 session 不存在，不会⾃动创建
    public String getSession2(@SessionAttribute(value = "userinfo", required = false)
                                          String request) {
        return request;
        // http://localhost:8080/test/getsession2
    }


    // 3. 返回数据
    // 3.1 返回静态页面. 在 RespController 类中实现.
    // 3.2 练习计算器功能 在 CalcController 类中实现.
    // 3.3 返回 JSON 对象
    @RequestMapping("/resJson")
    @ResponseBody
    public HashMap<String, String> resJson() {
        // hashMap 和 Json 格式一样, 都为 key-value 的键值对,
        // 因此用 hashMap 浏览器会自动转为 Json 对象.
        HashMap<String, String> map = new HashMap<>();
        map.put("Java", "Java Value");
        map.put("MySQL", "MySQL Value");
        map.put("Redis", "Redis Value");
        return map;
        // url: http://localhost:8080/test/resJson
        // 执行结果: {"Java":"Java Value","MySQL":"MySQL Value","Redis":"Redis Value"}
    }
}
